﻿using System;
using System.Linq;
using System.Linq.Expressions;

namespace HeroldIT.Dnn.Modules
{
    /// <summary>
    /// Provides extension methods for building custom predicate expressions.
    /// </summary>
    public static class PredicateBuilder
    {
        /// <summary>
        /// Returns an expression resulting in <c>true</c>.
        /// </summary>
        /// <typeparam name="T">The type to apply the predicate on.</typeparam>
        /// <returns>An expression resulting in <c>true</c>.</returns>
        public static Expression<Func<T, bool>> True<T>() { return f => true; }

        /// <summary>
        /// Returns an expression resulting in <c>false</c>.
        /// </summary>
        /// <typeparam name="T">The type to apply the predicate on.</typeparam>
        /// <returns>An expression resulting in <c>false</c>.</returns>
        public static Expression<Func<T, bool>> False<T>() { return f => false; }

        /// <summary>
        /// Returns an expression combining the two given expressions with an Or operator.
        /// </summary>
        /// <typeparam name="T">The type to apply the predicate on.</typeparam>
        /// <returns>An expression combining the two given expressions with an Or operator.</returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                            Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        /// <summary>
        /// Returns an expression combining the two given expressions with an And operator.
        /// </summary>
        /// <typeparam name="T">The type to apply the predicate on.</typeparam>
        /// <returns>An expression combining the two given expressions with an And operator.</returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                             Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    }
}
